package com.slmn.model;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTreeProperty;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.slmn.antlr.MsonBaseListener;
import com.slmn.antlr.MsonParser;
import com.slmn.antlr.MsonParser.ValueContext;
import com.slmn.antlr.MsonParser.*;

public class DefPhase extends MsonBaseListener {
	static private final Logger logger = LoggerFactory
			.getLogger(DefPhase.class);
	public ParseTreeProperty<Scope> scopes = new ParseTreeProperty<Scope>();
	Deque<PairContext> stack = new ArrayDeque<>();
	List<String> objects = new ArrayList<>();
	List<String> packages = new ArrayList<>();
	Set<String> keywords = new TreeSet<>();
	Set<String> ids = new TreeSet<>();
	SymbolTable symtab;
	Scope currentScope; // define symbols in this scope
	private boolean isKeywords = false;

	public DefPhase(SymbolTable symtab) {
		this.symtab = symtab;
		currentScope = symtab.globals;
	}

	@Override
	public void exitMson(@NotNull MsonContext ctx) {
		logger.info("Tables = {}", objects);
		logger.info("Packages = {}", packages);
		logger.info("Keywords = {}", keywords);
		logger.info("{}", ids);
	}

	@Override
	public void enterDomainClass(@NotNull DomainClassContext ctx) {
		DomainHeaderContext dhctx = ctx.domainHeader();
		String objectName = dhctx.getChild(dhctx.getChildCount() - 1).getText();
		objects.add(objectName);
		// def class but leave superclass blank until ref phase
		ClassSymbol cs = new ClassSymbol(objectName, currentScope, null);
		cs.def = ctx;              // point from symbol table into parse tree
		saveScope(ctx, cs);        // point from parse tree into symbol table
        currentScope.define(cs);   // def class in current scope
        currentScope = cs;         // set current scope to class scope
	}

	@Override
	public void exitDomainClass(@NotNull DomainClassContext ctx) {
		currentScope = currentScope.getEnclosingScope(); // pop scope
	}
	
	@Override
	public void enterDomainArray(@NotNull DomainArrayContext ctx) {
		DomainHeaderContext dhctx = (DomainHeaderContext) ctx.getChild(0);
		String pn = dhctx.getChild(dhctx.getChildCount() - 1).getText();
		if (pn.equals("KEYWORDS")) {
			isKeywords = true;
		}
	}

	@Override
	public void exitDomainArray(@NotNull DomainArrayContext ctx) {
		isKeywords = false;
	}

	@Override
	public void enterPackage(@NotNull PackageContext ctx) {
		ParserRuleContext prc = ctx.getParent();
		if (!(prc instanceof DomainArrayContext)) {
			return;
		}
		DomainArrayContext dac = (DomainArrayContext) prc;
		DomainHeaderContext dhctx = (DomainHeaderContext) dac.getChild(0);
		IdContext ic = (IdContext) dhctx.getChild(dhctx.getChildCount() - 1);
		String pn = ic.getText();
		packages.add(pn);
		// def class but leave superclass blank until ref phase
		ClassSymbol cs = new ClassSymbol(pn, currentScope, null);
		cs.def = ic;              // point from symbol table into parse tree
		saveScope(ic, cs);        // point from parse tree into symbol table
        currentScope.define(cs);  // def class in current scope
        currentScope = cs;        // set current scope to class scope
	}

	@Override
	public void exitPackage(@NotNull PackageContext ctx) {
		currentScope = currentScope.getEnclosingScope(); // pop scope
	}
	
	@Override
	public void enterKeyword(@NotNull MsonParser.KeywordContext ctx) {
		if (!isKeywords) {
			String kw = ctx.getChild(0).getText();
			keywords.add(kw);
		}
	}

	@Override
	public void enterDomainValue(@NotNull DomainValueContext ctx) {
		DomainHeaderContext dhctx = (DomainHeaderContext) ctx.getChild(0);
		if (!(ctx.getChild(2) instanceof ValueContext)) {
			return;
		}
		ValueContext vc = (ValueContext) ctx.getChild(2);
		IdContext ic;
		IdArrayContext iac;
		if (vc.getChild(0) instanceof IdContext) {
			ic = (IdContext) vc.getChild(0);
		} else if (vc.getChild(0) instanceof IdArrayContext) {
			iac = (IdArrayContext) vc.getChild(0);
		} else {
			return;
		}
		ids.add(vc.getChild(0).getText());
	}

	void saveScope(ParserRuleContext ctx, Scope s) {
		scopes.put(ctx, s);
	}

}
